﻿using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace CgdataBase
{
    public class DataHelper : IDisposable
    {
        private static DataHelper _instance;
        public static DataHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DataHelper();
                }

                return _instance;
            }
        }

        private IFreeSql freeSql;
        public static IFreeSql DB => Instance.freeSql;

        public void Initialize(FreeSql.DataType type, string connectionString, bool autoSyncStructure)
        {
            freeSql = new FreeSql.FreeSqlBuilder()
                .UseConnectionString(type, connectionString)
                .UseLazyLoading(true)
                .UseMonitorCommand(cmd => Debug.WriteLine(cmd.CommandText))
                .UseAutoSyncStructure(autoSyncStructure)
                .Build();

            //freeSql.UseJsonMap();
        }

        public int AddDataInfo<T>(T info) where T : class, IBaseInfo
        {
            return (int)freeSql.Insert<T>().AppendData(info).ExecuteIdentity();
        }

        public void AddDataInfos<T>(IEnumerable<T> items) where T : class, IBaseInfo
        {
            freeSql.Insert<T>().AppendData(items).ExecuteAffrows();
        }

        public void UpdateDataInfo<T>(T info) where T : class, IBaseInfo
        {
            freeSql.Update<T>()
                .SetSource(info)
                .ExecuteAffrows();
        }

        public void UpdateDataInfos<T>(IEnumerable<T> items) where T : class, IBaseInfo
        {
            freeSql.Update<T>()
                .SetSource(items)
                .ExecuteAffrows();
        }

        public void DeleteDataInfo<T>(T info) where T : class, IBaseInfo
        {
            freeSql.Delete<T>()
                .Where(s => s.ID == info.ID)
                .ExecuteAffrows();
        }

        public void DeleteDataInfos<T>(IEnumerable<T> items) where T : class, IBaseInfo
        {
            var ids = items.Select(s => s.ID).ToArray();
            freeSql.Delete<T>()
                .Where(s => ids.Contains(s.ID))
                .ExecuteAffrows();
        }

        public void DeleteDataInfos<T>(Expression<Func<T, bool>> exp) where T : class, IBaseInfo
        {
            freeSql.Delete<T>().Where(exp).ExecuteAffrows();
        }

        public List<T> SelectAll<T>() where T : class, IBaseInfo
        {
            return freeSql.Select<T>().ToList();
        }

        public List<T> SelectAll<T, TKey>(Expression<Func<T, TKey>> column, bool descending = false) where T : class, IBaseInfo
        {
            if (descending == false)
            {
                return freeSql.Select<T>().OrderBy(column).ToList();
            }
            else
            {
                return freeSql.Select<T>().OrderByDescending(column).ToList();
            }
        }

        public List<T> Select<T>(Expression<Func<T, bool>> exp) where T : class, IBaseInfo
        {
            return freeSql.Select<T>().Where(exp).ToList();
        }

        public T First<T>(Expression<Func<T, bool>> exp = null) where T : class, IBaseInfo
        {
            if (exp != null)
            {
                return freeSql.Select<T>().Where(exp).First();
            }
            else
            {
                return freeSql.Select<T>().First();
            }
        }

        public List<T> Select<T, TKey>(Expression<Func<T, bool>> exp, Expression<Func<T, TKey>> column, bool descending = false) where T : class, IBaseInfo
        {
            if (descending == false)
            {
                return freeSql.Select<T>().Where(exp).OrderBy(column).ToList();
            }
            else
            {
                return freeSql.Select<T>().Where(exp).OrderByDescending(column).ToList();
            }
        }

        public T SelectByID<T>(int id) where T : class, IBaseInfo
        {
            return freeSql.Select<T>().Where(s => s.ID.Equals(id)).First();
        }

        public List<T> SelectBySql<T>(string sqlText) where T : class, IBaseInfo
        {
            return freeSql.Select<T>().WithSql(sqlText).ToList();
        }

        public void Dispose()
        {
            freeSql?.Dispose();
        }

        public bool Any<T>(Expression<Func<T, bool>> exp) where T : class, IBaseInfo
        {
            return freeSql.Select<T>().Where(exp).Any();
        }

        public static string GetMySqlConnectionString(string address, int port, string username, string password, string databaseName)
        {
            return $"Data Source={address};Port={port};User ID={username};Password={password};Initial Catalog={databaseName};Charset=utf8;SslMode=none;Min pool size=1";
        }

        public static string GetSqliteConnectionString(string filePath)
        {
            return $"Data Source={filePath}";
        }

        public static void SynchronousData<T>(DataHelper src, DataHelper dst) where T : class, IBaseInfo
        {
            var items = src.SelectAll<T>();
            if (items.HadItems())
            {
                dst.AddDataInfos(items);
            }
        }
    }

    public interface IBaseInfo
    {
        int ID { get; set; }
    }
}